package top.lingkang.ciyuanwaf.controller;

import cn.hutool.core.io.FileUtil;
import lombok.extern.slf4j.Slf4j;
import org.noear.solon.annotation.Controller;
import org.noear.solon.annotation.Inject;
import org.noear.solon.annotation.Mapping;
import org.noear.solon.core.handle.Context;
import org.noear.solon.core.handle.MethodType;
import org.noear.solon.core.handle.ModelAndView;
import top.lingkang.ciyuanwaf.WebApp;
import top.lingkang.ciyuanwaf.dao.ConfHistoryDao;
import top.lingkang.ciyuanwaf.dao.SettingDao;
import top.lingkang.ciyuanwaf.dto.ResponseResult;
import top.lingkang.ciyuanwaf.entity.ConfHistoryEntity;
import top.lingkang.ciyuanwaf.entity.SettingEntity;
import top.lingkang.ciyuanwaf.utils.CheckUtils;
import top.lingkang.ciyuanwaf.utils.IdUtils;
import top.lingkang.ciyuanwaf.utils.terminal.TerminalUtils;
import top.lingkang.ciyuanwaf.vo.ServerItemVO;
import top.lingkang.hibernate6.transaction.Transaction;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * @author lingkang
 * created by 2023/11/27
 */
@Slf4j
@Controller
@Mapping("/nginx/server")
public class NginxServerController {
    @Inject
    private SettingDao settingDao;
    @Inject
    private ConfHistoryDao confHistoryDao;

    /**
     * 服务配置页面
     */
    @Mapping(value = "/config", method = MethodType.GET)
    public Object config() {
        ModelAndView view = new ModelAndView("serverConfig.ftl");
        SettingEntity entity = settingDao.getSetting();
        if (!"true".equals(entity.getIsInit())) {
            return new ResponseResult<>().fail("还未绑定nginx，无法访问此页");
        }
        String path = entity.getConfList();
        File file = new File(path);
        if (file.exists()) {
            List<ServerItemVO> list = new ArrayList<>();
            for (File item : file.listFiles()) {
                ServerItemVO vo = new ServerItemVO();
                vo.setName(item.getName());
                vo.setSize(item.length());
                vo.setLastUpdateTime(new Date(item.lastModified()));
                list.add(vo);
            }
            view.put("list", list);
        }
        view.put("nginxExistsUpdate", "true".equals(entity.getExistsUpdate()));
        return view;
    }

    /**
     * 添加服务配置弹窗
     */
    @Mapping(value = "/addConfig", method = MethodType.GET)
    public Object addServerConfig() {
        SettingEntity setting = settingDao.getSetting();
        if (!"true".equals(setting.getIsInit()))
            return new ResponseResult<>().fail("还未绑定nginx");
        return new ModelAndView("addServerConfig.ftl");
    }

    /**
     * 添加服务配置
     */
    @Transaction
    @Mapping(value = "/addConfig", method = MethodType.POST)
    public Object addServerConfig(String name, String content) {
        CheckUtils.checkNotNull(name, "配置文件名称");
        SettingEntity setting = settingDao.getSetting();
        if (!"true".equals(setting.getIsInit()))
            return new ResponseResult<>().fail("还未绑定nginx");
        TerminalUtils.cdNginxDir(WebApp.thread, setting.getNginxPath());
        name += ".conf";
        File file = new File(setting.getConfList());
        if (file.exists()) {
            for (String n : file.list()) {
                if (n.equals(name)) {
                    return new ResponseResult<>().fail("已经存在同名配置文件：" + file.getAbsolutePath() + File.separator + name);
                }
            }
        } else
            file.mkdirs();

        ConfHistoryEntity history = new ConfHistoryEntity();
        history.setId(IdUtils.id());
        history.setFileName(name);

        File conf = new File(file.getAbsolutePath() + File.separator + name);
        log.info("add server file: {}", conf.getAbsolutePath());
        if (!conf.exists())
            try {
                log.info("create server file: {}", conf.createNewFile());
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        history.setOldConf("");// 原配置，新建的为空
        FileUtil.writeString(content, conf, StandardCharsets.UTF_8);

        history.setNewConf(content);
        history.setStatus("配置检查通过");
        history.setType("add");
        confHistoryDao.saveOrUpdate(history);

        // 执行nginx检查
        String check = TerminalUtils.nginxCheck(WebApp.thread);
        if (!check.contains("successful")) {// 失败时回滚
            // 回滚处理
            history.setStatus("配置检查不通过");
            history.setExeResult(check);
            confHistoryDao.saveOrUpdate(history);

            // 回滚文件内容，直接删除
            conf.delete();

            log.error("添加配置失败：{}", check);
            return new ResponseResult<>().fail("添加配置失败：" + check);
        }

        setting.setExistsUpdate("true");
        settingDao.saveOrUpdate(setting);
        return new ResponseResult<>().success("添加配置成功：" + name);
    }

    /**
     * 删除服务配置
     */
    @Transaction
    @Mapping(value = "/deleteConfig", method = MethodType.POST)
    public Object deleteServerConfig(String name) {
        CheckUtils.checkNotNull(name, "配置文件名称");
        SettingEntity setting = settingDao.getSetting();
        if (!"true".equals(setting.getIsInit()))
            return new ResponseResult<>().fail("还未绑定nginx");
        File fileList = new File(setting.getConfList());
        String content = "";
        if (fileList.exists()) {
            for (File file : fileList.listFiles())
                if (file.getName().equals(name)) {
                    content = FileUtil.readString(file, StandardCharsets.UTF_8);
                    file.delete();
                    break;
                }
        }

        ConfHistoryEntity history = new ConfHistoryEntity();
        history.setId(IdUtils.id());
        history.setFileName(name);
        history.setStatus("配置检查通过");
        history.setType("delete");
        history.setOldConf(content);
        confHistoryDao.saveOrUpdate(history);
        setting.setExistsUpdate("true");
        settingDao.saveOrUpdate(setting);
        return new ResponseResult<>().success("删除成功！");
    }

    /**
     * 编辑服务配置弹窗
     */
    @Mapping(value = "/editConfig", method = MethodType.GET)
    public Object editServerConfig(String name) {
        CheckUtils.checkNotNull(name, "配置文件名称");
        SettingEntity setting = settingDao.getSetting();
        if (!"true".equals(setting.getIsInit()))
            return new ResponseResult<>().fail("还未绑定nginx");
        File fileList = new File(setting.getConfList());
        File content = null;
        if (fileList.exists()) {
            for (File file : fileList.listFiles())
                if (file.getName().equals(name)) {
                    content = file;
                    break;
                }
        }
        if (content == null)
            return new ResponseResult<>().fail("文件不存在: " + name);
        return new ModelAndView("addServerConfig.ftl")
                .put("edit", "edit")
                .put("name", name)
                .put("content", FileUtil.readString(content, StandardCharsets.UTF_8));
    }

    /**
     * 编辑服务配置
     */
    @Transaction
    @Mapping(value = "/editConfig", method = MethodType.POST)
    public Object editServerConfig(String name, String content) {
        CheckUtils.checkNotNull(name, "配置文件名称");
        SettingEntity setting = settingDao.getSetting();
        if (!"true".equals(setting.getIsInit()))
            return new ResponseResult<>().fail("还未绑定nginx");
        TerminalUtils.cdNginxDir(WebApp.thread, setting.getNginxPath());
        File file = new File(setting.getConfList());
        File serverConf = new File(file.getAbsolutePath() + File.separator + name);
        if (!serverConf.exists()) {
            log.warn("文件不存在，直接创建：" + name);
            return new ResponseResult<>().fail("配置文件不存在: " + name);
        }

        ConfHistoryEntity history = new ConfHistoryEntity();
        history.setId(IdUtils.id());
        history.setFileName(name);

        String oldConf = FileUtil.readString(serverConf, StandardCharsets.UTF_8);
        history.setOldConf(oldConf);
        // 覆盖内容
        FileUtil.writeString(content, serverConf, StandardCharsets.UTF_8);

        history.setNewConf(content);
        history.setStatus("配置检查通过");
        history.setType("edit");
        confHistoryDao.saveOrUpdate(history);

        // 执行nginx检查
        String check = TerminalUtils.nginxCheck(WebApp.thread);
        if (!check.contains("successful")) {// 失败时回滚
            // 回滚处理
            history.setStatus("配置检查不通过");
            history.setExeResult(check);
            confHistoryDao.saveOrUpdate(history);

            // 回滚文件内容
            FileUtil.writeString(content, serverConf, StandardCharsets.UTF_8);

            log.error("编辑配置失败：{}", check);
            return new ResponseResult<>().fail("编辑配置失败：" + check);
        }

        setting.setExistsUpdate("true");
        settingDao.saveOrUpdate(setting);
        return new ResponseResult<>().success("编辑配置成功：" + name);
    }

    @Mapping(value = "/download", method = MethodType.GET)
    public void download(Context context) {
        SettingEntity setting = settingDao.getSetting();
        File conf = new File(setting.getConfList());
        if (!conf.exists() || Objects.requireNonNull(conf.list()).length == 0) {
            context.outputAsJson(new ResponseResult<>().fail("未找到配置文件目录：" + conf.getAbsolutePath()).toJsonString());
            return;
        }
        if (Objects.requireNonNull(conf.list()).length == 0) {
            context.outputAsJson(new ResponseResult<>().fail("配置目录下没有配置：" + conf.getAbsolutePath()).toJsonString());
            return;
        }
        File tempZip = null;
        try {
            tempZip = File.createTempFile(
                    "server-" + new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss").format(new Date()),
                    ".zip");
            log.info("temp file: {}", tempZip.getAbsolutePath());
            FileOutputStream fos = new FileOutputStream(tempZip);
            ZipOutputStream zos = new ZipOutputStream(fos);
            for (File file : conf.listFiles()) {
                zos.putNextEntry(new ZipEntry(file.getName()));
                FileInputStream fis = new FileInputStream(file);
                byte[] buffer = new byte[1024];
                int len;
                while ((len = fis.read(buffer)) > 0) {
                    zos.write(buffer, 0, len);
                }
                fis.close();
                zos.closeEntry();
            }
            zos.close();
            fos.close();
            context.outputAsFile(tempZip);
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            if (tempZip != null && tempZip.exists()) {
                log.info("delete temp file: {}", tempZip.getAbsolutePath());
                tempZip.delete();
            }
        }
    }

}
